﻿using SiMay.Sockets.Delegate;
using SiMay.Sockets.Tcp.Awaitable;
using SiMay.Sockets.Tcp.Pooling;
using SiMay.Sockets.Tcp.Session;
using SiMay.Sockets.Tcp.TcpConfiguration;
using SiMay.Sockets.UtilityHelper;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SiMay.Sockets.Tcp.Server
{
    /// <summary>
    /// SiMay.Socket服务端
    /// </summary>
    public class TcpSocketSaeaServer : TcpSocketSaeaEngineBased
    {
        /// <summary>
        /// Socket对象
        /// </summary>
        private Socket ServerSocket;

        /// <summary>
        /// 是否开启监听
        /// </summary>
        private bool _isRuning = false;

        /// <summary>
        /// 服务配置
        /// </summary>
        private TcpSocketSaeaServerConfiguration _config;

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="saeaSessionType">接收数据模式</param>
        /// <param name="configuration">配置信息</param>
        /// <param name="completetionNotify">异步回调事件</param>
        internal TcpSocketSaeaServer(TcpSocketSaeaSessionType saeaSessionType, TcpSocketSaeaServerConfiguration configuration, NotifyEventHandler<TcpSocketCompletionNotify, TcpSocketSaeaSession> completetionNotify)
            : base(saeaSessionType, configuration, completetionNotify)
        {
            this._config = configuration;
            //如果模式是Packet(自动分包)而且开启心跳监测
            if (configuration.AppKeepAlive && saeaSessionType == TcpSocketSaeaSessionType.Packet)
            {
                //在后台执行心跳包检测线程
                var keepAliveThread = new Thread(AppKeepAliveWorkThread);
                keepAliveThread.IsBackground = true;
                keepAliveThread.Start();
            }
        }


        public IPEndPoint LocalEndPoint
        {
            get { return (IPEndPoint)ServerSocket.LocalEndPoint; }
        }

        /// <summary>
        /// 给所有连接发送心跳包
        /// </summary>
        private void AppKeepAliveWorkThread()
        {
            byte[] emptyHeart = new byte[] { 0, 0, 0, 0 };

            while (this._isRuning)
            {
                //循环所有连接的Session
                for (int i = 0; i < TcpSocketSaeaSessions.Count; i++)
                {
                    var session = (TcpSocketSaeaPackBased)TcpSocketSaeaSessions[i];
                    if (session.State != TcpSocketConnectionState.Connected)
                    {
                        LogHelper.WriteLog("server_heart_thread != Connected");
                        TcpSocketSaeaSessions.RemoveAt(i); i--;
                        continue;
                    }
                    //间隔5秒发送1次
                    if ((int)(DateTime.Now - session.LastReceiveDataTime).TotalSeconds > 5)
                    {
                        //如果5秒没有收到数据请求，则认为连接已经断开
                        LogHelper.WriteLog("server_heart_thread_timeout --state：" + session.State.ToString() + " present_time：" + DateTime.Now.ToString() + " heart_time：" + session.LastReceiveDataTime.ToString());
                        if (session.State == TcpSocketConnectionState.Connected)
                        {
                            Console.WriteLine("server timeout--");
                            session.Close(true);
                            TcpSocketSaeaSessions.RemoveAt(i); i--;
                        }
                    }
                }
                
                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// 开启监听
        /// </summary>
        /// <param name="ipendPoint">服务器的IP及端口信息</param>
        public void Listen(IPEndPoint ipendPoint)
        {
            this._isRuning = true;

            ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            SetSocketOptions();
            ServerSocket.Bind(ipendPoint);
            ServerSocket.Listen(_config.PendingConnectionBacklog);

            //从异步操作对象池中取出一个异步操作对象
            var awaiter = HandlerSaeaPool.Take();

            //开始接受连接
            SaeaExHelper.AcceptAsync(ServerSocket, awaiter, Accept);
        }

        /// <summary>
        /// 接受连接成功的回调方法
        /// </summary>
        /// <param name="awaiter">Socket异步方法对象</param>
        /// <param name="socketError">异步套接字操作的结果</param>
        private void Accept(SaeaAwaiter awaiter, SocketError socketError)
        {
            if (socketError == SocketError.Success)
            {
                //如果有连接成功，保存接受到连接的Socket
                var acceptedSocket = awaiter.Saea.AcceptSocket;
                //从Session取出一个Session
                var session = SessionPool.Take();

                //添加一个连接Session
                session.Attach(acceptedSocket);
                this.TcpSocketSaeaSessions.Add(session);

                //触发客户端连接成功回调
                this.CompletetionNotify?.Invoke(TcpSocketCompletionNotify.OnConnected, session);

                //开始接收数据
                session.StartProcess();
            }
            else
                LogHelper.WriteLog("server_accept-fail");

            if (!this._isRuning)
                return;

            awaiter.Saea.AcceptSocket = null;
            SaeaExHelper.AcceptAsync(ServerSocket, awaiter, Accept);
        }

        /// <summary>
        /// 设置Socket
        /// </summary>
        private void SetSocketOptions()
        {
            ServerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, Configuration.ReuseAddress);
        }

        /// <summary>
        /// 释放
        /// </summary>
        public override void Dispose()
        {
            try
            {
                ServerSocket.Close(0);
                ServerSocket = null;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog("server_shutdown exception info：" + e.Message);
            }
            finally
            {
                this._isRuning = false;
                base.Dispose();
            }
        }
    }
}
